{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Chap7 Linked Lists\n",
    "\n",
    "这里只做了单双链表的部分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:56.721313Z",
     "start_time": "2019-08-02T00:39:56.714619Z"
    }
   },
   "outputs": [],
   "source": [
    "from typing import Any\n",
    "from toydata.LinkedLists import Singlellist, Doublellist"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Reinforcement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-7.1\n",
    "Give an algorithm for finding the second-to-last node in a singly linked\n",
    "list in which the last node is indicated by a next reference of None."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:56.812551Z",
     "start_time": "2019-08-02T00:39:56.725090Z"
    }
   },
   "outputs": [],
   "source": [
    "def get_second_to_last(sll: Singlellist) -> Any:\n",
    "    ptr = sll.head\n",
    "    while ptr.next.next is not None:\n",
    "        ptr = ptr.next\n",
    "    return ptr.value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:56.962559Z",
     "start_time": "2019-08-02T00:39:56.817321Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3])\n",
    "get_second_to_last(sll)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-7.2\n",
    "Describe a good algorithm for concatenating two singly linked lists l1 and\n",
    "l2, given only references to the first node of each list, into a single list L that contains all the nodes of l1 followed by all the nodes of l2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.056051Z",
     "start_time": "2019-08-02T00:39:56.965291Z"
    }
   },
   "outputs": [],
   "source": [
    "def concatenating_linked_lists(l1: Singlellist,\n",
    "                               l2: Singlellist,\n",
    "                               ) -> Singlellist:\n",
    "    result = Singlellist()\n",
    "    result.head = l1.head\n",
    "    l1.tail.next = l2.head\n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.157319Z",
     "start_time": "2019-08-02T00:39:57.062726Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SLL[1, 2, 3, 4, 5, 6]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1 = Singlellist([1, 2, 3])\n",
    "l2 = Singlellist([4, 5, 6])\n",
    "concatenating_linked_lists(l1, l2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-7.3 \n",
    "Describe a recursive algorithm that counts the number of nodes in a singly\n",
    "linked list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.253816Z",
     "start_time": "2019-08-02T00:39:57.160876Z"
    }
   },
   "outputs": [],
   "source": [
    "def count_num(sll: Singlellist) -> int:\n",
    "    if sll.head is None:\n",
    "        return 0\n",
    "    sll.remove_first()\n",
    "    return 1 + count_num(sll)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.331740Z",
     "start_time": "2019-08-02T00:39:57.257237Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3])\n",
    "count_num(sll)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-7.4\n",
    "Describe in detail how to swap two nodes x and y (and not just their con-\n",
    "tents) in a singly linked list L given references only to x and y. Repeat\n",
    "this exercise for the case when L is a doubly linked list. Which algorithm\n",
    "takes more time?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "解答参考[这里](https://www.geeksforgeeks.org/swap-nodes-in-a-linked-list-without-swapping-data/), 注意一点就是这里互换的是节点而不是简单地改变对应的值。这里以单链表为例进行说明。\n",
    "\n",
    "![](../datas/chap7-swap.jpg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.420485Z",
     "start_time": "2019-08-02T00:39:57.334410Z"
    }
   },
   "outputs": [],
   "source": [
    "# single linked list\n",
    "def sll_swap_node(sll: Singlellist, x, y) -> None:\n",
    "    if x == y:\n",
    "        return sll\n",
    "    # find prev_x, curr_x\n",
    "    ptr = sll.head\n",
    "    while ptr.next.value != x:\n",
    "        ptr = ptr.next\n",
    "    prev_x = ptr\n",
    "    curr_x = ptr.next\n",
    "\n",
    "    # S1: find prev_y, curr_y\n",
    "    ptr = sll.head\n",
    "    while ptr.next.value != y:\n",
    "        ptr = ptr.next\n",
    "    prev_y = ptr\n",
    "    curr_y = ptr.next\n",
    "\n",
    "    # S2: change prev\n",
    "    if prev_x is None:  # x is the head\n",
    "        sll.head = curr_y\n",
    "    else:\n",
    "        prev_x.next = curr_y\n",
    "\n",
    "    if prev_y is None:  # y is the head\n",
    "        sll.head = curr_x\n",
    "    else:\n",
    "        prev_y.next = curr_x\n",
    "\n",
    "    # S3: change next\n",
    "    temp = curr_x.next\n",
    "    curr_x.next = curr_y.next\n",
    "    curr_y.next = temp"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.532361Z",
     "start_time": "2019-08-02T00:39:57.426873Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SLL[1, 4, 3, 2, 5]"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "sll_swap_node(sll, 2, 4)\n",
    "sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.620013Z",
     "start_time": "2019-08-02T00:39:57.535146Z"
    }
   },
   "outputs": [],
   "source": [
    "def dll_swap_node(dll: Doublellist, x, y) -> None:\n",
    "    if x == y:\n",
    "        return\n",
    "\n",
    "    # S1: find node x\n",
    "    ptr = dll._header\n",
    "    while ptr._element != x:\n",
    "        ptr = ptr._next\n",
    "    curr_x = ptr\n",
    "\n",
    "    # S1: find node y\n",
    "    ptr = dll._header\n",
    "    while ptr._element != y:\n",
    "        ptr = ptr._next\n",
    "    curr_y = ptr\n",
    "\n",
    "    # S2: change prev\n",
    "    temp = curr_x._prev\n",
    "    curr_x._prev = curr_y._prev\n",
    "    curr_y._prev._next = curr_x\n",
    "    curr_y._prev = temp\n",
    "    temp._next = curr_y\n",
    "\n",
    "    # S3: change next\n",
    "    temp = curr_x._next\n",
    "    curr_x._next = curr_y._next\n",
    "    curr_y._next._prev = curr_x\n",
    "    curr_y._next = temp\n",
    "    temp._prev = curr_y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:39:57.736542Z",
     "start_time": "2019-08-02T00:39:57.621881Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Header->1->4->3->2->5->Tailer"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dll = Doublellist([1, 2, 3, 4, 5])\n",
    "dll_swap_node(dll, 2, 4)\n",
    "dll"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面进行水平对比："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:40:06.327113Z",
     "start_time": "2019-08-02T00:39:57.738619Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.04 µs ± 20.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "%timeit sll_swap_node(sll, 2, 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:40:17.523485Z",
     "start_time": "2019-08-02T00:40:06.331072Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.39 µs ± 50.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "dll = Doublellist([1, 2, 3, 4, 5])\n",
    "%timeit dll_swap_node(dll, 2, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "hint说单链表要慢一些，上面测试发现两者差不多...据指点，可能是因为Python是解释性语言？"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### R-7.9 \n",
    "Give a fast algorithm for concatenating two doubly linked lists L and M,\n",
    "with header and trailer sentinel nodes, into a single list $L^{'}$ ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:40:17.531626Z",
     "start_time": "2019-08-02T00:40:17.526029Z"
    }
   },
   "outputs": [],
   "source": [
    "def concatenating_dll(l1: Doublellist,\n",
    "                      l2: Doublellist) -> Doublellist:\n",
    "    l1._tailer._prev._next = l2._header._next\n",
    "    l2._header._next._prev = l1._tailer._prev\n",
    "    return l1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:40:17.636877Z",
     "start_time": "2019-08-02T00:40:17.533969Z"
    }
   },
   "outputs": [],
   "source": [
    "l1 = Doublellist([1, 2, 3])\n",
    "l2 = Doublellist({4, 5, 6})\n",
    "l1 = concatenating_dll(l1, l2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T00:40:17.719110Z",
     "start_time": "2019-08-02T00:40:17.639379Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Header->1->2->3->4->5->6->Tailer"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "l1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Creativity"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-7.24 \n",
    "Give a complete implementation of the stack ADT using a singly linked\n",
    "list that includes a header sentinel."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "之前在[ToyData库](https://github.com/shenxiangzhuang/ToyData/blob/master/toydata/Stack.py)，我们实现过`LinkedStack`不过在那里并没有使用`header sentinel`, 其实都是差不多的，这里就不再写了，不用`sentinel`的代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T01:10:35.522229Z",
     "start_time": "2019-08-02T01:10:35.511216Z"
    }
   },
   "outputs": [],
   "source": [
    "class LinkedStack(Singlellist):\n",
    "    \"\"\"\n",
    "    LIFO Srtack implementation using Python list as underlyinh storage.\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        \"\"\"Create and empty stack.\n",
    "        \"\"\"\n",
    "        self._data = Singlellist()\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"Return the number of elements in the stack.\n",
    "        Time Complexity: O(1)\n",
    "        \"\"\"\n",
    "        return len(self._data)\n",
    "\n",
    "    def __repr__(self):\n",
    "        \"\"\"\n",
    "        Show the stack properly.\n",
    "        Time Complexity: O(n)\n",
    "        \"\"\"\n",
    "        if self.is_empty():\n",
    "            s1 = '| ' + \"\".center(5) + ' |' + '\\n'\n",
    "            s2 = '-' * 9\n",
    "            return s1 + s2\n",
    "        else:\n",
    "            s = []\n",
    "            for i in range(len(self._data) - 1, -1, -1):\n",
    "                ele = self._data[i]\n",
    "                s1 = '| ' + ele.__repr__().center(5) + ' |' + '\\n'\n",
    "                s2 = '-' * 9 + '\\n'\n",
    "                s.append(s1 + s2)\n",
    "            return ''.join(s)\n",
    "\n",
    "    def is_empty(self):\n",
    "        \"\"\"Return True if the stack is empty\n",
    "        Time Complexity: O(1)\n",
    "        \"\"\"\n",
    "        return len(self._data) == 0\n",
    "\n",
    "    def push(self, e):\n",
    "        \"\"\"Add element to the top of the stack\n",
    "        Time Complexity: O(1)\n",
    "        Note: \"*\" in here means amortization\n",
    "        \"\"\"\n",
    "        self._data.add_first(e)\n",
    "\n",
    "    def top(self):\n",
    "        \"\"\"\n",
    "        Return (but not remove) at the top of the stack.\n",
    "        Raise Empty exception if the stack in empty.\n",
    "        Time Complexity: O(1)\n",
    "        \"\"\"\n",
    "        if self.is_empty():\n",
    "            raise Empty(\"Stack in empty!\")\n",
    "        return self._data.head.value\n",
    "\n",
    "    def pop(self):\n",
    "        \"\"\"\n",
    "        Remove and return the element from the top of the stack(LIFO)\n",
    "        Raise Empty exception if the stack is empty.\n",
    "        Time Complexity: O(1)\n",
    "        \"\"\"\n",
    "        if self.is_empty():\n",
    "            raise Empty(\"Stack is empty!\")\n",
    "        ele = self._data.head.value\n",
    "        self._data.remove_first()\n",
    "        return ele"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-7.28 \n",
    "Describe a fast recursive algorithm for reversing a singly linked list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T02:57:30.437983Z",
     "start_time": "2019-08-02T02:57:30.427072Z"
    }
   },
   "outputs": [],
   "source": [
    "# 复制节点数据的方法\n",
    "def reverse_sll_1(sll: Singlellist) -> Singlellist:\n",
    "    if len(sll) == 1:\n",
    "        return sll\n",
    "    head = sll.remove_first()\n",
    "    reverse_sll_1(sll).add_last(head.value)\n",
    "    return sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T02:57:59.693920Z",
     "start_time": "2019-08-02T02:57:59.681660Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SLL[5, 4, 3, 2, 1]"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "reverse_sll_1(sll)\n",
    "sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T02:58:59.341163Z",
     "start_time": "2019-08-02T02:58:59.333931Z"
    }
   },
   "outputs": [],
   "source": [
    "# 移动节点的方法\n",
    "def reverse_sll_2(sll: Singlellist) -> Singlellist:\n",
    "    if len(sll) == 1:\n",
    "        return sll\n",
    "    head = sll.remove_first()\n",
    "    head.next = None\n",
    "    reverse_sll_2(sll).tail.next = head\n",
    "    sll.tail = head\n",
    "    return sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T02:59:11.682236Z",
     "start_time": "2019-08-02T02:59:11.675710Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SLL[5, 4, 3, 2, 1]"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "reverse_sll_2(sll)\n",
    "sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T03:00:09.425269Z",
     "start_time": "2019-08-02T03:00:00.490518Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8.01 µs ± 189 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n",
      "301 ns ± 7.35 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)\n"
     ]
    }
   ],
   "source": [
    "# 水平对比\n",
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "%timeit reverse_sll_1(sll)\n",
    "\n",
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "%timeit reverse_sll_2(sll)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到后面的方法快的多，毕竟来回复制数据的花销比较大。\n",
    "\n",
    "在一开始写的时候，我其实是写错了的，最后用`PySnooper`调试才发现了问题，代码是这样的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T03:02:19.782469Z",
     "start_time": "2019-08-02T03:02:19.775108Z"
    }
   },
   "outputs": [],
   "source": [
    "def reverse_sll_wrong(sll: Singlellist) -> Singlellist:\n",
    "    if len(sll) == 1:\n",
    "        return sll\n",
    "    head = sll.remove_first()\n",
    "    return reverse_sll_wrong(sll).add_last(head.value)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "初看上去感觉很符合逻辑，但是运行会出Bug, 原因在于，我们的`reverse_sll_wrong`第二个`return`返回的不是单链表本身，而是单链表执行`add_last`返回的值（None）。所以在开始的时候堆栈的时候是没有问题的，但是到了后面`return`的时候，就会出现对`None`调用`add_last`方法的错误。\n",
    "\n",
    "所以我们只要简单的进行修改及可以了，即将最后的`return`的表达式先执行，之后返回链表本身即可，改后就是`reverse_sll_1`函数那样。\n",
    "\n",
    "写递归还是第一次遇见这种错误，这里记录下。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### C-7.29\n",
    "Describe in detail an algorithm for reversing a singly linked list L using\n",
    "only a constant amount of additional space and not using any recursion."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T03:37:41.256806Z",
     "start_time": "2019-08-02T03:37:41.247952Z"
    }
   },
   "outputs": [],
   "source": [
    "def reverse_sll_loop(sll: Singlellist) -> Singlellist:\n",
    "    if len(sll) == 1:\n",
    "        return sll\n",
    "    head = sll.head\n",
    "    prev_node = None\n",
    "    curr_node = sll.head\n",
    "    while curr_node is not None:\n",
    "        next_node = curr_node.next\n",
    "        curr_node.next = prev_node\n",
    "        prev_node = curr_node\n",
    "        curr_node = next_node\n",
    "\n",
    "    sll.head = sll.tail\n",
    "    sll.tail = head"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-02T03:37:57.529599Z",
     "start_time": "2019-08-02T03:37:57.512585Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SLL[5, 4, 3, 2, 1]"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sll = Singlellist([1, 2, 3, 4, 5])\n",
    "reverse_sll_loop(sll)\n",
    "sll"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "250.433px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
